﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Threading;

namespace VPLink2013.Framework.Extensions
{
    public class Tuple<T1, T2>
    {
        public T1 First { get; private set; }
        public T2 Second { get; private set; }
        internal Tuple(T1 first, T2 second)
        {
            First = first;
            Second = second;
        }
    }

    public class DbFilter 
    {
        public string Name { get; set; }
        public string Value { get; set; }
        public string Operator { get; set; }
    }



    public static partial class FrameworkExtensions
    {
        public static bool IsNullOrEmpty(this string[] input)
        {
            try
            {
                if (input == null)
                {
                    return true;
                }
                if (input.Length == 0)
                {
                    return true;
                }

                return false;
            }
            catch (Exception)
            {
                //TODO : log
                return false;
            }
        }

        public static bool IsNullOrEmpty(this string input)
        {
            try
            {

                if (input == null)
                {
                    return true;
                }

                if (System.String.IsNullOrEmpty(input))
                {
                    return true;
                }

                return false;
            }

            catch (Exception)
            {
                //TODO : log
                return false;
            }
        }

        public static bool IsNullOrEmptyOrWhitespace(this string input)
        {
            try
            {

                if (input.IsNullOrEmpty())
                {
                    return true;
                }

                input = input.Replace(" ", string.Empty);
                if (input.IsNullOrEmpty())
                {
                    return true;
                }

                return false;
            }

            catch (Exception)
            {
                //TODO : log
                return false;
            }
        }

        public static bool IsNotNullOrEmptyOrWhitespace(this string input)
        {
            return input.IsNullOrEmptyOrWhitespace() == false;
        }

        public static bool ToBoolean(this string input)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return false;
                }

                if (input.Equals("true", StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }

                if (input.Equals("vrai", StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }

                if (input.Equals("oui", StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }

                bool result = false;
                if (System.Boolean.TryParse(input, out result))
                {
                    return result;
                }

                return false;
            }
            catch (Exception)
            {
                //TODO : log
                return false;
            }
        }

        public static bool SafeEquals(this string input, string stringToCompare, StringComparison stringComparison)
        {
            try
            {
                if (input == null)
                {
                    return false;
                }

                if (stringToCompare == null)
                {
                    return false;
                }

                return input.Equals(stringToCompare, stringComparison);

            }
            catch (Exception)
            {
                //TODO : log
                return false;
            }
        }

        public static bool SafeEquals(this string input, string stringToCompare)
        {
            return input.SafeEquals(stringToCompare, StringComparison.CurrentCultureIgnoreCase);
        }

        public static string SafeTrim(this string input)
        {
            try
            {
                if (input == null)
                {
                    return string.Empty;
                }

                string trimmedInput = input.Trim();
                if (trimmedInput == null)
                {
                    return string.Empty;
                }

                return trimmedInput;

            }
            catch (Exception)
            {
                //TODO : log
                return input;
            }

        }

        public static bool Contains(this string input, string stringToCompare, StringComparison stringComparison)
        {
            try
            {
                if (input.IsNullOrEmpty())
                {
                    return false;
                }

                if (stringToCompare.IsNullOrEmpty())
                {
                    return false;
                }

                if (stringComparison == StringComparison.CurrentCultureIgnoreCase)
                {
                    input = input.ToLower();
                    stringToCompare = stringToCompare.ToLower();
                }

                if (stringComparison == StringComparison.InvariantCultureIgnoreCase)
                {
                    input = input.ToLower();
                    stringToCompare = stringToCompare.ToLower();
                }

                return input.Contains(stringToCompare);


            }
            catch (Exception)
            {
                //TODO : log
                return false;
            }
        }

        public static bool DoesNotContain(this string input, string stringToCompare, StringComparison stringComparison)
        {
            return input.Contains(stringToCompare, stringComparison) == false;
        }

        public static bool DoesNotContain(this string input, string stringToCompare)
        {
            return input.Contains(stringToCompare, StringComparison.CurrentCultureIgnoreCase) == false;
        }

        public static string ToEmptyIfNull(this string input)
        {
            try
            {
                if (input == null)
                {
                    return string.Empty;
                }

                return input;

            }
            catch (Exception)
            {
                //TODO :log
                return input;
            }
        }

        public static bool SafeStartsWith(this string input, string stringToCompare)
        {
            try
            {
                if (input.IsNullOrEmpty())
                {
                    return false;
                }

                if (stringToCompare.IsNullOrEmpty())
                {
                    return false;
                }

                return input.StartsWith(stringToCompare, StringComparison.CurrentCultureIgnoreCase);
            }
            catch (Exception)
            {
                //TODO : log
                return false;
            }
            
        }

        public static bool DoesNotStartWith(this string input, string stringToCompare)
        {
            return input.SafeStartsWith(stringToCompare) == false;
        }


        public static bool SafeEndsWith(this string input, string stringToCompare)
        {
            try
            {
                if (input.IsNullOrEmpty())
                {
                    return false;
                }

                if (stringToCompare.IsNullOrEmpty())
                {
                    return false;
                }

                return input.EndsWith(stringToCompare, StringComparison.CurrentCultureIgnoreCase);
            }
            catch (Exception)
            {
                //TODO : log
                return false;
            }

        }

        public static bool DoesNotEndWith(this string input, string stringToCompare)
        {
            return input.SafeEndsWith(stringToCompare) == false;
        }


        /// <summary>
        /// Get the right part of a string formatted like "leftpart separator rightpart". The separator character should appear 0 or 1 time only.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="separator">any string character</param>
        /// <returns></returns>
        public static string SplitRightFromSingleSeparator(this string input, string separator)
        {
            try
            {
                if (input.IsNullOrEmpty())
                {
                    return null;
                }

                if (separator.IsNullOrEmpty())
                {
                    return null;
                }

                string[] separators = new string[] { separator };
                string[] splittedInput = input.Split(separators, StringSplitOptions.RemoveEmptyEntries);

                if (splittedInput.IsNullOrEmpty())
                {
                    return null;
                }

                if (splittedInput.Length == 1)
                {
                    return null;
                }

                string rightPart = splittedInput[1];
                rightPart = rightPart.SafeTrim();

                return rightPart;


            }
            catch (Exception)
            {
                //TODO : log
                return null;
            }
        }

        /// <summary>
        /// Get the left part of a string formatted like "leftpart separator rightpart". The separator character should appear 0 or 1 time only.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="separator">any string character</param>
        /// <returns></returns>
        public static string SplitLeftFromSingleSeparator(this string input, string separator)
        {
            try
            {
                if (input.IsNullOrEmpty())
                {
                    return null;
                }

                if (separator.IsNullOrEmpty())
                {
                    return input;
                }

                string[] separators = new string[] { separator };
                string[] splittedInput = input.Split(separators, StringSplitOptions.RemoveEmptyEntries);

                if (splittedInput.IsNullOrEmpty())
                {
                    return input;
                }

                string leftPart = splittedInput[0];
                leftPart = leftPart.SafeTrim();

                return leftPart;

            }
            catch (Exception)
            {
                //TODO : log
                return input;
            }
        }

        public static Tuple<string,string> SplitFromSingleSeparator(this string input, string separator)
        {
            try
            {

                if (input.IsNullOrEmpty())
                {
                    return null;
                }

                string leftpart = input.SplitLeftFromSingleSeparator(separator);
                string rightPart = input.SplitRightFromSingleSeparator(separator);
                return new Tuple<string, string>(leftpart, rightPart);

            }
            catch (Exception)
            {
                //TODO : log
                return null;
            }
        }

        public static List<string> SplitToList(this string input, string separator)
        {
            try
            {
                if (input == null)
                {
                    return null;
                }

                List<string> splittedStrings = new List<string>();
                if (separator.IsNullOrEmpty())
                {
                    splittedStrings.Add(input);
                    return splittedStrings;
                }

                string[] separators = new string[] { separator };
                string[] splittedInput = input.Split(separators, StringSplitOptions.RemoveEmptyEntries);

                if (splittedInput.IsNullOrEmpty())
                {
                    return null;
                }

                foreach (var item in splittedInput)
                {
                    string split = item.SafeTrim();
                    splittedStrings.Add(split);
                }

                return splittedStrings;

            }
            catch (Exception)
            {
                //TODO : log
                return null;
            }
        }

        public static int ToLength(this string input)
        {
            try
            {
                if (input == null)
                {
                    return 0;
                }

                return input.Length;
            }
            catch (Exception ex)
            {
                //TODO : log
                return 0;
            }
        }



        public static string ToJson(this string input)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return string.Empty;
                }

                input = input.Replace("\\", @"\\"); //replace \ with \\
                input = input.Replace("\"", @"\"""); //replace " with \"
                input = input.Replace("\n", @"\n"); //replace newline with \n
                input = input.Replace("\r", @"\r"); //replace return with \r
                input = input.Replace("\t", @"\t"); //replace tab with \t
                input = input.Replace("\f", @"\f"); //replace formfeed with \f
                //input = input.Replace("/", @"\/"); //replace / with \/


                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return string.Empty;
                }

                return input;

            }
            catch (Exception ex)
            {
                //TODO : log
                return input;
            }
        }


        public static bool IsInteger(this string input)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return false;
                }

                Int32 integerValue = 0;
                if (System.Int32.TryParse(input, out integerValue))
                {
                    return true;
                }

                Int64 longValue = 0;
                if (System.Int64.TryParse(input, out longValue))
                {
                    return true;
                }

                return false;

            }
            catch (Exception ex)
            {
                //TODO : log
                return false;
            }
        }

        public static bool IsNotInteger(this string input)
        {
            return input.IsInteger() == false;
        }


        /// <summary>
        /// Converts a string into a DateTime.
        /// </summary>
        /// <param name="input">Input string.</param>
        /// <param name="dateFormat">If input is like '15/06/2008 08:30' dateFormat="g". If input is like '15/06/2008' dateFormat="d" </param>
        /// <param name="culture">String representing a culture. For French should be "fr-FR". If culture is null or empty the culture is taken from current thread culture</param>
        /// <returns></returns>
        public static DateTime? ToSmallDateTime(this string input, string dateFormat, string culture)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return null;
                }

                if (dateFormat.IsNullOrEmptyOrWhitespace())
	            {
		            return null;
	            }

                CultureInfo provider  = Thread.CurrentThread.CurrentCulture;

                if (culture.IsNotNullOrEmptyOrWhitespace())
                {
                    provider = new CultureInfo(culture);
                }

                

                DateTime result = System.DateTime.ParseExact(input, dateFormat, provider);
                return result;
            }
            catch (Exception)
            {
                //TODO : log
                return null;
            }
        }

        public static DbFilter ToDbFilter(this string input)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return null;
                }

                string[] operators = new string[] { "eq", "le", "ge" };

                foreach (var item in operators)
                {
                    string splitter = string.Format(@" {0} ",item);
                    List<string> values = input.SplitToList(splitter);
                    // values should contains 2 items only (left operand + right operand)
                    if (values.IsNullOrEmpty())
                    {
                        continue;
                    }

                    if (values.Count != 2)
                    {
                        continue;
                    }

                    if (values.IsNotNullOrEmpty())
                    {
                        var dbFilter = new DbFilter()
                        {
                            Name = values[0],
                            Value = values[1],
                            Operator = item
                        };
                        return dbFilter;
                    }
                }


                //TODO : process other operators
                return null;


            }
            catch (Exception ex)
            {
                //TODO : log
                return null;
            }
        }

        public static int ToInt32(this string input)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return 0;
                }

                Int32 integerValue = 0;
                if (System.Int32.TryParse(input, out integerValue))
                {
                    return integerValue;
                }

                return 0;

            }
            catch (Exception ex)
            {
                //TODO : log
                return 0;
            }
        }

        public static int? ToInt32OrNull(this string input)
        {
            try
            { 
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return null;
                }

                input = input.Trim();
                input = input.Replace(" ", "");

                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return null;
                }

                char lastChar = input[input.Length - 1];
                if (lastChar == 128)
                {
                    input = input.Substring(0, input.Length - 1);
                }

                Int32 integerValue = 0;
                if (System.Int32.TryParse(input, out integerValue))
                {
                    return integerValue;
                }

                return null;

            }
            catch (Exception ex)
            {
                //TODO : log
                return null;
            }
        }

        public static bool IsInList(this string input, List<string> list)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return false;
                }

                if (list.IsNullOrEmpty())
                {
                    return false;
                }

                if (list.SafeContains(input,StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }

                return false;

            }
            catch (Exception ex)
            {
                //TODO : log
                return false;
            }
        }

        public static bool IsNotInList(this string input, List<string> list)
        {
            return input.IsInList(list) == false;
        }

        /// <summary>
        /// Converts a string into a Double
        /// </summary>
        /// <param name="input">Input string.</param>
        /// <param name="culture">String representing a culture. For French should be "fr-FR". If culture is null or empty the culture is taken from current thread culture</param>
        /// <returns></returns>
        public static double? ToDoubleOrNull(this string input, string culture)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return null;
                }

                input = input.Trim();
                input = input.Replace(" ", "");

                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return null;
                }

                char lastChar = input[input.Length - 1];
                if (lastChar == 128)
                {
                    input = input.Substring(0, input.Length - 1);
                }

                //string test = input.ToString();

                //char[] inputArray = input.ToCharArray();
                //char[] testArray = test.ToCharArray();

                CultureInfo provider = Thread.CurrentThread.CurrentCulture;

                if (culture.IsNotNullOrEmptyOrWhitespace())
                {
                    provider = new CultureInfo(culture);
                }

                Double doubleValue = 0;
                if (System.Double.TryParse(input,NumberStyles.Any,provider, out doubleValue))
                {
                    return doubleValue;
                }

                return null;

            }
            catch (Exception ex)
            {
                //TODO : log
                return null;
            }
        }

        /// <summary>
        /// Converts a string into a month number.
        /// </summary>
        /// <param name="input">Input string representing the name of a month.</param>
        /// <param name="culture">String representing a culture. For French should be "fr-FR". If culture is null or empty the culture is taken from current thread culture</param>
        /// <returns>Return a number between 1 (janvier) and 12(decembre)</returns>
        public static int? ToMonthNumber(this string input,  string culture)
        {
            try
            {
                if (input.IsNullOrEmptyOrWhitespace())
                {
                    return null;
                }

                CultureInfo originalCultureInfo = Thread.CurrentThread.CurrentCulture;
                
                if (culture.IsNotNullOrEmptyOrWhitespace())
                {
                    Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
                }
                int currentYear = System.DateTime.Now.Year;
                for (int i = 1; i <= 12; i++)
                {
                    DateTime myDate = new System.DateTime(currentYear, i,1);
                    string month = myDate.ToString("MMM");


                    if (month.StartsWith(input, StringComparison.CurrentCultureIgnoreCase))
                    {
                        Thread.CurrentThread.CurrentCulture = originalCultureInfo;
                        return i;
                    }

                    //check for accentuated characters (be tolerant on orthograph)
                    if (month.Contains("é"))
                    {
                        month = month.Replace("é", "e");
                        if (month.StartsWith(input, StringComparison.CurrentCultureIgnoreCase))
                        {
                            Thread.CurrentThread.CurrentCulture = originalCultureInfo;
                            return i;
                        }
                    }
                    

                    if (month.Contains("û"))
                    {
                        month = month.Replace("û", "u");
                        if (month.StartsWith(input, StringComparison.CurrentCultureIgnoreCase))
                        {
                            Thread.CurrentThread.CurrentCulture = originalCultureInfo;
                            return i;
                        }
                    }
                    


                }

                Thread.CurrentThread.CurrentCulture = originalCultureInfo;

                return null;
                
                
            }
            catch (Exception)
            {
                //TODO : log
                return null;
            }
        }


    }
}
